import UIBase from "./UIBase"
import LayerMgr from "./LayerMgr"
import AppUtil from "../AppUtil"
import AppConstants from "../AppConstants"
import ResMgr from "../manager/ResMgr"
import TopBlock from "./TopBlock"


class UIOpen {
    constructor(public uiCls: any, public args: any[]) { }
}


/**
 * @description UI 管理器
 * @author mirahs
 * @email 2892727493@qq.com
 */
export default class UIMgr {
    private static _uis: Map<string, UIBase> = new Map()
    private static _current: UIBase

    // 主UI名称
    private static _uiMain: string
    // UI 打开记录
    private static _uiOpens: UIOpen[] = []

    // 记录是 open 还是 back
    private static _isOpen: boolean
    private static _openData: UIOpen


    public static init(uiMain: string) {
        this._uis.clear()

        this._uiMain = uiMain
        this._uiOpens = []
    }

    /**
     * 打开 UI
     * @param uiCls UI 类
     * @param uiArgs UI 参数
     * @returns 
     */
    public static open(uiCls: any, ...uiArgs: any[]) {
        const cls = new uiCls() as UIBase
        const viewName = cls.getClassName()

        if (this._current && this._current.uiName == viewName) {
            console.error(`UIMgr.open 已在当前UI:${viewName}`)
            return
        }

        this._isOpen = true
        this._openData = new UIOpen(uiCls, uiArgs)

        this.show(cls, uiArgs)
    }

    /**
     * 回退至上一个 UI
     * @returns 
     */
    public static back() {
        if (this._uiOpens.length < 2) {
            console.error('UIMgr.back 数据错误')
            return
        }

        this._isOpen = false

        // 上一个 UI
        const prev = this._uiOpens[this._uiOpens.length - 2]

        this.show(new prev.uiCls, prev.args)
    }

    /** 判断是否当前 UI */
    public static isAlive(uiCls: any): boolean {
        const viewName = AppUtil.getClassName(uiCls)

        return this._current != null && viewName == this._current.uiName
    }

    /** 更新 UI */
    public static updateView(uiCls: any, ...args: any[]): boolean {
        if (!this.isAlive(uiCls)) return false

        this._current.updateView(args)

        return true
    }


    private static async show(cls: UIBase, uiArgs: any[]) {
        TopBlock.show()

        const viewName = cls.getClassName()

        let current = this._uis.get(viewName)
        if (!current) {
            current = cls
            current.uiName = viewName

            let skinNode: cc.Node

            if (current.bundleName == 'null') {
                const uiPrefab = cc.find(current.skinPath, cc.Canvas.instance.node)
                if (!uiPrefab) {
                    TopBlock.hide()
                    console.error(`UIMgr.show cc.find 找不到[${viewName}]面板场景对象:${current.skinPath}`)
                    return
                }
                skinNode = cc.instantiate(uiPrefab)
                uiPrefab.active = false
            } else {
                const [uiPrefab, err] = await AppUtil.asyncWrap<cc.Prefab>(ResMgr.load(current.bundleName, current.skinPath))
                if (err) {
                    TopBlock.hide()
                    console.error(`UIMgr.show loadRes skinPath:${current.skinPath} err:${err}`)
                    return
                }
                skinNode = cc.instantiate(uiPrefab)
            }

            current.init(uiArgs)
            current.skin = skinNode
            current.initDone()
        } else {
            current.reset(uiArgs)
        }

        this.startShowUI(current)
    }

    private static startShowUI(go: UIBase) {
        go.showing()
        // 挂载 Widget 组件, 保持跟 Canvas 一样大小
        AppUtil.setWidget(go.skin)
        // 添加对象到 UI层级
        LayerMgr.setLayer(go.skin, AppConstants.viewLayer.UI)

        if (go.uiShowStyle == AppConstants.uiShowStyle.Normal || !this._current || go.duration <= 0) {
            this.showNormal(go)
            return
        }

        switch (go.uiShowStyle) {
            case AppConstants.uiShowStyle.FadeIn:
                this.showFadeIn(go)
                break
            default:
                console.error(`UIMgr.startShowUI uiShowStyle 错误:${go.uiShowStyle}`)
                break
        }
    }

    private static hideLast(duration?: number) {
        const last = this._current
        if (!last) return

        this._current = null
        last.hiding()

        if (!duration) {
            this.hideUI(last)
        } else {
            cc.tween(last.skin).
                to(duration, { opacity: 0 }).
                call(() => this.hideUI(last)).
                start()
        }
    }

    private static hideUI(go: UIBase) {
        go.skin.active = false
        go.hided()

        if (go.cache) return

        this._uis.delete(go.uiName)
        go.destroy()
    }

    private static showUIAfter(current: UIBase) {
        TopBlock.hide()

        this._current = current

        if (!this._uis.has(current.uiName)) {
            this._uis.set(current.uiName, current)
        }

        if (this._isOpen) {
            if (this._uiMain == current.uiName) {
                this._uiOpens = []
            }
            this._uiOpens.push(this._openData)
        } else if (!this._isOpen) {
            // 回退后, 清除最后一个 UI
            this._uiOpens.splice(this._uiOpens.length - 1, 1)
        }
    }


    //#region 显示方式
    /** 默认 */
    private static showNormal(go: UIBase) {
        go.skin.opacity = 255
        go.skin.active = true

        go.showed()

        this.hideLast()
        this.showUIAfter(go)
    }

    /** 渐入 */
    private static showFadeIn(go: UIBase) {
        go.skin.opacity = 0
        go.skin.active = true

        this.hideLast(go.duration)

        cc.tween(go.skin).
            to(go.duration, { opacity: 255 }).
            call(() => {
                go.showed()

                this.showUIAfter(go)
            }).
            start()
    }
    //#endregion
}
